# coding=utf-8

import os
import json
import copy
import random

from base_tool import WK_PATH, get_host_ip, is_domain

class Config(object):

    """
    提供一个简单的配置类
    应该向外单独暴露初始化接口，方便安装程序调用，配置的初始化应当只进行一次。
    向内暴露配置读写以及应用增减接口。
    
    """

    config_path = os.path.join(WK_PATH,"config.json")
    ports = range(8082,9091)
    config_cache = None

    # # app_status -1/0/1/2
    # # 0 已安装
    # # 1 调试运行
    # # 2 正式运行
    # # -1 已停止

    app_config_template = {
        "app_id":"",
        "app_mode":"",
        "app_name":"",
        "app_status":"",
        "app_host":"",
        "app_run_port":"",
        "app_run_workers":"",
        "app_work_path":""
    }

    def __init__(self):
        if os.path.isfile(self.config_path):
            self.read()


    @classmethod
    def initial(cls,admin_mail=None,admin_password=None,admin_salt=None,admin_domain=None):
        for i in [admin_mail,admin_password,admin_salt,admin_domain]:
            if not bool(i):
                print("初始化参数缺失")
                return False

        initial_config = {
            "admin_mail":admin_mail,
            "admin_password":admin_password,
            "admin_salt":admin_salt,
            "server_ip_addrs":[],
            "admin_domain":admin_domain,
            "apps":{}
        }
        initial_config["server_ip_addrs"].append(get_host_ip())
        # 保存初始化结果
        cls.write(json.dumps(initial_config))
        return True


    @classmethod
    def write(cls,string):
        """
        覆盖写配置
        @config String
        """
        with open(cls.config_path, 'wt') as f:
            f.write(string)
        # 每次写入后都通过读取配置来重建缓存
        cls.read()

    @classmethod
    def read(cls):
        """
        读配置
        """
        with open(cls.config_path, 'rt') as f:
            cls.config_cache = json.loads(f.read())
            return cls.config_cache
    
    @classmethod
    def get_all_using_ports(cls):
        """
        读所有已用端口
        """
        apps_list = cls.config_cache.get("apps")
        if apps_list:
            return [i["app_run_port"] for i in apps_list.values()]
        else:
            return []
    
    @classmethod
    def get_all_free_ports(cls):
        """
        读所有未用端口
        """
        return set(cls.ports).difference(set(cls.get_all_using_ports()))
    
    @classmethod
    def get_a_free_port(cls):
        """
        读一个未用端口
        """
        return random.choice(list(cls.get_all_free_ports()))
    
    @classmethod
    def add_app(cls,app_id=None,app_mode=None,app_name=None,app_status=None,app_host=None,app_run_port=None,app_run_workers=None,app_work_path=None):
        """
        在配置中增加一个应用
        """

        t = copy.copy(cls.app_config_template)
        t["app_id"] = app_id
        t["app_mode"] = app_mode
        t["app_name"] = app_name
        t["app_status"] = app_status
        t["app_host"] = app_host
        t["app_run_port"] = app_run_port
        t["app_run_workers"] = app_run_workers
        t["app_work_path"] = app_work_path
        cls.config_cache["apps"][app_id] = t
        cls.write(json.dumps(cls.config_cache))
        return True

    @classmethod
    def update_app(cls,app_id=None,**kwargs):
        """
        在配置中更新一个应用
        """

        a = copy.copy(cls.config_cache)
        t = a["apps"].get(app_id)
        if not t:
            return False
        
        if "app_id" in kwargs:
            t["app_id"] = kwargs.get("app_id")
        if "app_name" in kwargs:
            t["app_name"] = kwargs.get("app_name")
        if "app_mode" in kwargs:
            t["app_mode"] = kwargs.get("app_mode")
        if "app_status" in kwargs:
            t["app_status"] = kwargs.get("app_status")
        if "app_host" in kwargs:
            t["app_host"] = kwargs.get("app_host")
        if "app_run_port" in kwargs:
            t["app_run_port"] = kwargs.get("app_run_port")
        if "app_run_workers" in kwargs:
            t["app_run_workers"] = kwargs.get("app_run_workers")
        if "app_work_path" in kwargs:
            t["app_work_path"] = kwargs.get("app_work_path")
        
        cls.config_cache["apps"][app_id] = t
        print(t)
        cls.write(json.dumps(cls.config_cache))
        return True

    
    @classmethod
    def delete_app(cls,app_id=None):
        """
        在配置中移除一个应用
        """
        
        if app_id in cls.config_cache["apps"]:
            del cls.config_cache["apps"][app_id]
        cls.write(json.dumps(cls.config_cache))
        return True


class Caddy_config(object):
    """
    提供caddyserver配置文件的处理接口
    """
    @staticmethod
    def rebuild():
        app_caddy_file = os.path.join(WK_PATH,"config","caddy","app_caddy_file")
        base_caddy_file = os.path.join(WK_PATH,"config","caddy","base_caddy_file")
        runtime_caddy_file = os.path.join(WK_PATH,"config","caddy","runtime_caddy_file")
        
        # 读取基础配置模板，覆盖写入
        with open(base_caddy_file,"rt",encoding="utf-8")as bs:
            admin_mail = Config().config_cache.get("admin_mail")
            domain = Config().config_cache.get("admin_domain")
            port = 8081
            static_root = os.path.join(WK_PATH,"static")
            proxy_path = "/"
            log_path = os.path.join(WK_PATH,"caddy_log.txt")
            bs_finished = str(bs.read()).format(domain=domain,port=port,proxy_path=proxy_path,log_path=log_path,mail=admin_mail,static_root=static_root)
            bs_finished = bs_finished.replace("[","{").replace("]","}")
            
            with open(runtime_caddy_file,"wt",encoding="utf-8")as ru:
                ru.write(bs_finished)
            
            # 遍历 app ，追加写入
            apps = Config().config_cache["apps"]
            if bool(apps):
                with open(runtime_caddy_file,"at",encoding="utf-8")as ru:
                    with open(app_caddy_file,"rt",encoding="utf-8")as ap:

                        for app in apps.values():
                            app_mode = app.get("app_mode")
                            if app_mode == "0": continue
                            app_domain = app.get("app_host")
                            if not is_domain(raw=app_domain): continue
                            app_static_path = os.path.join(app.get("app_work_path"),"static")
                            app_proxy_path = "/"
                            app_port = app.get("app_run_port")
                            
                            ap_finished = str(ap.read()).format(app_domain=app_domain,app_static_path=app_static_path,app_proxy_path=app_proxy_path,app_port=app_port,admin_mail=admin_mail)
                            ap_finished = ap_finished.replace("[","{").replace("]","}")
                            ru.write(ap_finished)
                            
        with open(runtime_caddy_file,"rt",encoding="utf-8")as ru:
            return str(ru.read())
            

                
